#pragma once

#include <muduo/net/TcpServer.h>
#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/Buffer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <unordered_map>
#include <mutex>
#include <iostream>
#include <string>
#include "detail.hpp"
#include "fields.hpp"
#include "abstract.hpp"
#include "message.hpp"

namespace zl
{
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;

        MuduoBuffer(muduo::net::Buffer *buf)
            : _buf(buf)
        {
        }
        virtual ssize_t readableSize() override
        {
            return _buf->readableBytes();
        }
        virtual int32_t peekInt32() override
        {
            // 网络字节序->主机字节序
            // 后面接收要改
            return _buf->peekInt32();
        }
        virtual void retrieveInt32() override
        {
            return _buf->retrieveInt32();
        }
        virtual int32_t readInt32() override
        {
            return _buf->readInt32();
        }
        virtual std::string retrieveAsString(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }

    private:
        muduo::net::Buffer *_buf;
    };

    class BufferFactory
    {
    public:
        template <typename... Args>
        static BaseBuffer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
        }
    };

    class LVPtrtocol : public BaseProtocol
    {
    public:
        using ptr = std::shared_ptr<LVPtrtocol>;
        // |--Len--|--mtype--|--idlen--|--id--|--body--|
        //      4    4         4               body_len
        // 判断缓冲区中的数据量是否足够一条消息的处理
        virtual bool canProcessed(const BaseBuffer::ptr &buf) override
        {
            // 取出一条完整消息
            if (buf->readableSize() < lenFieldsLength)
            {
                return false;
            }
            int32_t total_len = buf->peekInt32();

            if (buf->readableSize() < (total_len + lenFieldsLength))
            {
                return false;
            }
            return true;
        }

        // |--Len--|--VALUE--|
        // |--Len--|--mtype--|--idlen--|--id--|--body--|
        virtual bool onMessage(const BaseBuffer::ptr &buf, BaseMessage::ptr &msg) override
        {
            // 当调用onMessage的时候，默认认为缓冲区中的数据足够一条完整的消息
            int32_t total_len = buf->readInt32();  // 总长度
            MType mtype = (MType)buf->readInt32(); // 数据类型
            int32_t idlen = buf->readInt32();      // id长度
            int32_t body_len = total_len - idlen - idFieldsLength - mtypeFieldsLength;
            std::string id = buf->retrieveAsString(idlen);
            std::string body = buf->retrieveAsString(body_len);
            msg = MessageFactory::create(mtype);
            if (msg.get() == nullptr)
            {
                ELOG("消息类型错误，构造消息对象失败");
                return false;
            }
            bool ret = msg->unserialize(body);
            if (ret == false)
            {
                ELOG("消息正文反序列化消息失败");
                return false;
            }
            msg->setId(id);
            msg->setMType(mtype);
            return true;
        }
        virtual std::string serialize(const BaseMessage::ptr &msg) override
        {
            // |--Len--|--mtype--|--idlen--|--id--|--body--|
            std::string body = msg->serialize();
            std::string id = msg->rid();

            auto mtype = htonl((int32_t)msg->mtype());
            int32_t idlen = htonl(id.size());
            int32_t h_total_len = mtypeFieldsLength + idFieldsLength + id.size() + body.size();
            int32_t n_total_len = htonl(h_total_len);

            std::string ret;
            // 用于预先分配字符串的存储空间
            ret.reserve(h_total_len);
            ret.append((char *)&n_total_len, lenFieldsLength);
            ret.append((char *)&mtype, mtypeFieldsLength);
            ret.append((char *)&idlen, idFieldsLength);
            ret.append(id);
            ret.append(body);

            return ret;
        }

    private:
        const size_t lenFieldsLength = 4;
        const size_t mtypeFieldsLength = 4;
        const size_t idFieldsLength = 4;
    };

    class ProtocolFactory
    {
    public:
        template <typename... Args>
        static BaseProtocol::ptr create(Args &&...args)
        {
            return std::make_shared<LVPtrtocol>(std::forward<Args>(args)...);
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;

        MuduoConnection(const muduo::net::TcpConnectionPtr &conn, BaseProtocol::ptr ptrtocol)
            : _conn(conn), _ptrtocol(ptrtocol)
        {
        }

        virtual void send(const BaseMessage::ptr &msg) override
        {
            std::string body = _ptrtocol->serialize(msg);
            _conn->send(body);
        }
        virtual void shutdown() override
        {
            _conn->shutdown();
        }
        virtual bool connected() override
        {
            return _conn->connected();
        }

    private:
        BaseProtocol::ptr _ptrtocol;
        muduo::net::TcpConnectionPtr _conn;
    };

    class ConnectionFactory
    {
    public:
        template <typename... Args>
        static BaseConnection::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };

    class MuduoServer : public BaserServer
    {
    public:
        using ptr = std::shared_ptr<MuduoServer>;
        MuduoServer(int port)
            : _server(&_loop, muduo::net::InetAddress("0.0.0.0", port), "MuduoServer", muduo::net::TcpServer::kReusePort), _ptrtocol(ProtocolFactory::create())
        {
        }

        virtual void start()
        {
            _server.setConnectionCallback(std::bind(&MuduoServer::onConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _server.start();
            _loop.loop();
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                std::cout << "连接建立\n";

                // 有很多连接，加锁unordered_map管理起来
                auto muduo_con = ConnectionFactory::create(conn, _ptrtocol);
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _connections.insert(std::make_pair(conn, muduo_con));
                }

                if (_cb_connection)
                    _cb_connection(muduo_con);
            }
            else
            {
                std::cout << "连接断开\n";
                BaseConnection::ptr muduo_con;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _connections.find(conn);
                    if (it == _connections.end())
                        return;

                    muduo_con = it->second;
                    _connections.erase(it);
                }

                if (_cb_close)
                    _cb_close(muduo_con);
            }
        }

        // 主控
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("连接处有数据，开始处理");

            auto base_buf = BufferFactory::create(buf);
            while (1)
            {
                // 数据不足
                if (_ptrtocol->canProcessed(base_buf) == false)
                {
                    // 一条数据过大或者多条不完整的消息过大
                    if (base_buf->readableSize() > maxDataSize)
                    {
                        conn->shutdown();
                        ELOG("数据长度过长，关闭连接");
                        return;
                    }
                    break;
                }
                // 数据开始正常处理
                BaseMessage::ptr msg;
                bool ret = _ptrtocol->onMessage(base_buf, msg);
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误");
                    return;
                }

                // 找到对应连接处理
                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _connections.find(conn);
                    if (it == _connections.end())
                    {
                        conn->shutdown();
                        return;
                    }

                    base_conn = it->second;
                }

                if (_cb_message)
                    _cb_message(base_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        BaseProtocol::ptr _ptrtocol;
        muduo::net::EventLoop _loop;
        muduo::net::TcpServer _server;
        std::mutex _mutex;
        //muduo::net::TcpConnectionPtr, BaseConnection::ptr
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _connections;
    };

    class ServerFactory
    {
    public:
        template <typename... Args>
        static BaserServer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };

    class MuduoClient : public BaseClient
    {
    public:
        using ptr = std::shared_ptr<MuduoClient>;
        MuduoClient(const std::string &ip, int port)
            : _loop(_loopthread.startLoop()), _client(_loop, muduo::net::InetAddress(ip, port), "MuduoClient"), _ptrtocol(ProtocolFactory::create()), _downlatch(1)
        {
        }

        virtual void connect() override
        {
            DLOG("设置回调函数,连接服务器");
            _client.setConnectionCallback(std::bind(&MuduoClient::onConnection, this, std::placeholders::_1));
            _client.setMessageCallback(std::bind(&MuduoClient::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _client.connect();
            _downlatch.wait();
            DLOG("连接服务器成功");
        }
        virtual void shutdown() override
        {
            return _client.disconnect();
        }
        virtual bool send(const BaseMessage::ptr &msg) override
        {
            if (connected() == false)
            {
                ELOG("连接断开");
                return false;
            }
            _connection->send(msg);
            return true;
        }
        virtual BaseConnection::ptr connection() override
        {
            return _connection;
        }
        virtual bool connected() override
        {
            return (_connection && _connection->connected());
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                std::cout << "连接建立\n";

                _downlatch.countDown();
                _connection = ConnectionFactory::create(conn, _ptrtocol);
            }
            else
            {
                std::cout << "连接断开\n";
                _connection.reset();
            }
        }

        // 主控
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("连接处有数据，开始处理");

            auto base_buf = BufferFactory::create(buf);
            while (1)
            {
                // 数据不足
                if (_ptrtocol->canProcessed(base_buf) == false)
                {
                    // 一条数据过大或者多条不完整的消息过大
                    if (base_buf->readableSize() > maxDataSize)
                    {
                        conn->shutdown();
                        ELOG("数据长度过长，关闭连接");
                        return;
                    }
                    break;
                }
                // 数据开始正常处理
                BaseMessage::ptr msg;
                bool ret = _ptrtocol->onMessage(base_buf, msg);
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误");
                    return;
                }

                if (_cb_message)
                    _cb_message(_connection, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        BaseProtocol::ptr _ptrtocol;
        BaseConnection::ptr _connection;
        muduo::CountDownLatch _downlatch;
        muduo::net::EventLoopThread _loopthread;
        muduo::net::EventLoop *_loop;
        muduo::net::TcpClient _client;
    };

    class ClientFactory
    {
    public:
        template <typename... Args>
        static BaseClient::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };

}
